{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "import triton\n",
    "import triton.language as tl\n",
    "import torch\n",
    "import os\n",
    "os.environ['TRITON_PRINT_AUTOTUNING']= '1'\n",
    "from flash_muon_cuda import matmul_transpose_assign, matmul_transpose\n",
    "import time\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# @triton.autotune([triton.Config({\"BLOCK_M\":bm, \"BLOCK_N\":bn, \"BLOCK_K\":bk, \"GROUP_SIZE_M\":gm}, num_warps=nw, num_stages=4)\n",
    "#                   for bm in [32, 64]\n",
    "#                   for bn in [32, 64]\n",
    "#                   for bk in [64]\n",
    "#                   for nw in [1,2,4,8]\n",
    "#                   for gm in [8]\n",
    "#                   ], key=[\"N\"])\n",
    "@triton.jit\n",
    "def matmul_kernel(X, \n",
    "                  Y, \n",
    "                  N: tl.constexpr,\n",
    "                  BLOCK_M:tl.constexpr=64,\n",
    "                  BLOCK_N:tl.constexpr=64,\n",
    "                  BLOCK_K:tl.constexpr=64,\n",
    "                  GROUP_SIZE_M:tl.constexpr=16,\n",
    "                  ):\n",
    "    pid = tl.program_id(axis=0)\n",
    "    num_pid_m = tl.cdiv(N, BLOCK_M)\n",
    "    num_pid_n = tl.cdiv(N, BLOCK_N)\n",
    "    num_pid_in_group = GROUP_SIZE_M * num_pid_n\n",
    "    group_id = pid // num_pid_in_group\n",
    "    first_pid_m = group_id * GROUP_SIZE_M\n",
    "    group_size_m = min(num_pid_m - first_pid_m, GROUP_SIZE_M)\n",
    "    pid_m = first_pid_m + ((pid % num_pid_in_group) % group_size_m)\n",
    "    pid_n = (pid % num_pid_in_group) // group_size_m\n",
    "\n",
    "    if (pid_m * BLOCK_M + BLOCK_M) <= (pid_n * BLOCK_N):\n",
    "        return\n",
    "\n",
    "    off_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)\n",
    "    off_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)\n",
    "    a_ptrs = X + off_m[:, None] * N + tl.arange(0, BLOCK_K)[None, :]\n",
    "    # b_ptrs = X + off_n[None, :] * N + tl.arange(0, BLOCK_K)[:, None]\n",
    "    b_ptrs = X + off_n[:, None] * N + tl.arange(0, BLOCK_K)[None, :]\n",
    "    acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)\n",
    "\n",
    "    for _ in range(0, tl.cdiv(N, BLOCK_K)):\n",
    "        b = tl.load(b_ptrs)\n",
    "        a = tl.load(a_ptrs)\n",
    "        # acc = tl.dot(a, b, acc)\n",
    "        acc = tl.dot(a, tl.permute(b, (1, 0)), acc)\n",
    "        a_ptrs += BLOCK_K\n",
    "        b_ptrs += BLOCK_K\n",
    "\n",
    "    y_ptrs1 = Y + off_m[:, None] * N + off_n[None, :]\n",
    "    tl.store(y_ptrs1, acc)\n",
    "\n",
    "    y_ptrs2 = Y + off_m[None, :] + off_n[:, None] * N\n",
    "    tl.store(y_ptrs2, tl.permute(acc, (1,0)))\n",
    "\n",
    "    # y_ptrs2 = Y + off_m[:, None] + off_n[None, :] * N\n",
    "    # tl.store(y_ptrs2, acc)\n",
    "\n",
    "def matmul(x, out=None):\n",
    "    if out is None:\n",
    "        out = torch.zeros_like(x)\n",
    "    M, N = x.shape\n",
    "    assert N % 128 == 0 and M % 128 == 0\n",
    "    kwargs = {\"BLOCK_M\":128 if N>=4096 else 64, \"BLOCK_N\":128 if N>=4096 else 64, \"BLOCK_K\":64, \"num_warps\":4, \"num_stages\":4, \"GROUP_SIZE_M\":8}\n",
    "    grid = lambda meta: (triton.cdiv(M, meta['BLOCK_M']) * triton.cdiv(N, meta['BLOCK_N']), )\n",
    "    matmul_kernel[grid](x, \n",
    "                        out, \n",
    "                        N,\n",
    "                        **kwargs\n",
    "                        )\n",
    "    return out\n",
    "\n",
    "a, b, c = (3.4445, -4.7750,  2.0315)\n",
    "def run_triton(X, step=5):\n",
    "    for _ in range(step):\n",
    "        A = matmul(X)\n",
    "        B = b * A + c * matmul(A)\n",
    "        X = a * X + B @ X\n",
    "    return X\n",
    "\n",
    "def run_torch(X, step=5):\n",
    "    for _ in range(step):\n",
    "        A = X @ X.T\n",
    "        B = b * A + c * A @ A\n",
    "        X = a * X + B @ X\n",
    "    return X\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "      dim    triton      torch       cuda\n",
      "0    1024  0.015140   0.010127   0.038926\n",
      "1    2048  0.035008   0.028196   0.075227\n",
      "2    3072  0.093735   0.078786   0.209389\n",
      "3    4096  0.131490   0.182103   0.274603\n",
      "4    5120  0.257187   0.388764   0.668624\n",
      "5    6144  0.401960   0.659991   0.987421\n",
      "6    7168  0.652907   1.038566   1.600684\n",
      "7    8192  0.945991   1.612713   2.040780\n",
      "8    9216  1.363409   2.252778   2.856188\n",
      "9   10240  1.805552   3.073045   4.144083\n",
      "10  11264  2.400649   4.177691   5.132962\n",
      "11  12288  3.163356   5.295420   6.708187\n",
      "12  13312  3.983519   6.739612   8.395350\n",
      "13  14336  4.998694   8.884090  10.290255\n",
      "14  15360  6.046372  10.737362  12.774410\n"
     ]
    }
   ],
   "source": [
    "device = \"cuda\"\n",
    "dtype = torch.bfloat16\n",
    "N = 1024\n",
    "x = torch.randn(N, N*2, device=device, dtype=dtype)\n",
    "\n",
    "data = {\"dim\":[], \"triton\":[], \"torch\":[], \"cuda\":[]}\n",
    "for i in range(1, 16):\n",
    "    N = i * 1024\n",
    "    x = torch.randn(N, N, device=device, dtype=dtype)\n",
    "    ms1 = triton.testing.do_bench(lambda: matmul_transpose(x))\n",
    "    ms2 = triton.testing.do_bench(lambda: matmul(x))\n",
    "    ms3 = triton.testing.do_bench(lambda: torch.matmul(x, x.t()))\n",
    "    data[\"dim\"].append(N)\n",
    "    data[\"triton\"].append(ms2)\n",
    "    data[\"torch\"].append(ms3)\n",
    "    data[\"cuda\"].append(ms1)\n",
    "df = pd.DataFrame(data)\n",
    "print(df)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.allclose(matmul(x), torch.matmul(x, x.t()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 1.5040e+04, -5.5750e+01,  2.5469e+00,  ...,  2.4100e+02,\n",
       "          3.4600e+02,  6.3438e+00],\n",
       "        [-5.5750e+01,  1.5040e+04, -2.1000e+02,  ...,  1.5062e+01,\n",
       "          2.1400e+02, -1.9625e+01],\n",
       "        [ 2.5469e+00, -2.1000e+02,  1.5680e+04,  ..., -2.9500e+01,\n",
       "         -1.5000e+02, -1.1700e+02],\n",
       "        ...,\n",
       "        [ 2.4100e+02,  1.5062e+01, -2.9500e+01,  ...,  1.5232e+04,\n",
       "         -2.1000e+02, -2.5625e+01],\n",
       "        [ 3.4600e+02,  2.1400e+02, -1.5000e+02,  ..., -2.1000e+02,\n",
       "          1.5616e+04,  4.5000e+00],\n",
       "        [ 6.3438e+00, -1.9625e+01, -1.1700e+02,  ..., -2.5625e+01,\n",
       "          4.5000e+00,  1.5360e+04]], device='cuda:0', dtype=torch.bfloat16)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.matmul(x, x.t())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.1314159482717514\n",
      "0.18525195121765137\n"
     ]
    }
   ],
   "source": [
    "print(triton.testing.do_bench(lambda: matmul(x)))\n",
    "print(triton.testing.do_bench(lambda: torch.matmul(x, x.t())))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAGxCAYAAACeKZf2AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAXxJJREFUeJzt3Xd4FPXbxeHPpieEJNRQDEV6J9JEsBJBBAQrTWmCL4hIUYGogKAQQEEUUBQVUGnqDxCVIiKoKEVQmlJEekmo6SFt5/1jZCEQIIEkk92c+7pWZmZndp/JYvYw8y02wzAMRERERFyEm9UFiIiIiOQkhRsRERFxKQo3IiIi4lIUbkRERMSlKNyIiIiIS1G4EREREZeicCMiIiIuReFGREREXIqH1QXkNbvdzvHjxylcuDA2m83qckRERCQLDMMgLi6OMmXK4OZ27WszBS7cHD9+nJCQEKvLEBERkRtw5MgRbrnllmvuY2m4+fnnn3nzzTfZsmULJ06cYPHixXTo0OGaxyQnJzNmzBg+//xzIiMjKV26NCNHjqRXr15Zes/ChQsD5g8nICDgZk9BRERE8kBsbCwhISGO7/FrsTTcJCQkUK9ePXr16sUjjzySpWOeeOIJoqKi+Pjjj6lcuTInTpzAbrdn+T0v3IoKCAhQuBEREXEyWWlSYmm4ad26Na1bt87y/itWrOCnn35i//79FC1aFIAKFSrkUnUiIiLijJyqt9TSpUtp2LAhEydOpGzZslStWpUXX3yRpKSkqx6TnJxMbGxshoeIiIi4LqdqULx//37WrVuHj48Pixcv5vTp0zz77LOcOXOGWbNmZXpMREQEo0ePzuNKRURExCo2wzAMq4sA8x7a9RoUt2zZkl9++YXIyEgCAwMBWLRoEY899hgJCQn4+vpecUxycjLJycmO9QsNkmJiYq7Z5iY9PZ3U1NQbPyG5gpeX13W774mIiGQmNjaWwMDA635/g5NduSldujRly5Z1BBuAGjVqYBgGR48epUqVKlcc4+3tjbe3d5bfwzAMIiMjiY6OzomS5RJubm5UrFgRLy8vq0sREREX5lThplmzZnz55ZfEx8fj7+8PwN69e3Fzc7tun/esuhBsSpYsiZ+fnwb6yyEXBk88ceIE5cqV089VRERyjaXhJj4+nn379jnWDxw4wNatWylatCjlypUjPDycY8eO8emnnwLQpUsXXn/9dXr27Mno0aM5ffo0L730Er169cr0llR2paenO4JNsWLFbvr1JKMSJUpw/Phx0tLS8PT0tLocERFxUZY2gNi8eTOhoaGEhoYCMGTIEEJDQxk5ciQAJ06c4PDhw479/f39WbVqFdHR0TRs2JCuXbvSrl073n333Ryp50IbGz8/vxx5Pcnowu2o9PR0iysRERFXZumVm3vuuYdrtWeePXv2FduqV6/OqlWrcrGqrA0QJNmnn6uIiOQFdV0RERERl6JwI4B5lSwoKMjqMkRERG6awo0A0LFjR/bu3Wt1GSIiIjdN4UYA8PX1pWTJklaXISIiTu7HH+EasyLlCYUbF3HPPffw/PPPM3ToUIoWLUqpUqV47bXXHM9PnjyZOnXqUKhQIUJCQnj22WeJj493PH/pbam9e/dis9nYvXt3hvd4++23qVSpkmN9586dtG7dGn9/f4KDg3nqqac4ffp0rp6niIjkX//+C61bQ/XqEBVlXR0KN9dhGJCQYM0juxNjzJkzh0KFCrFx40YmTpzImDFjHD3L3NzcePfdd/nrr7+YM2cOP/74I0OHDs30dapWrUrDhg2ZO3duhu1z586lS5cuAERHR3PfffcRGhrK5s2bWbFiBVFRUTzxxBPZ/yGLiIhLeOklSEkxw42lNwOMAiYmJsYAjJiYmCueS0pKMv7++28jKSnJsS0+3jDMmJH3j/j4rJ/X3XffbTRv3jzDtkaNGhnDhg3LdP8vv/zSKFasmGN91qxZRmBgoGP97bffNipVquRY37NnjwEYu3btMgzDMF5//XWjZcuWGV7zyJEjBmDs2bMn0/fM7OcrIiKu4ccfze8uNzfD2Lkz51//Wt/fl9OVGxdSt27dDOulS5fm5MmTAPzwww+0aNGCsmXLUrhwYZ566inOnDlDYmJipq/VqVMnDh48yIYNGwDzqs1tt91G9erVAdi2bRtr1qzB39/f8bjw3L///ptbpygiIvlQejoMHmwu9+0LtWpZW49TzS1lBT8/uKRpSp6/d3ZcPqWBzWbDbrdz8OBB2rZtS79+/Rg7dixFixZl3bp1PP3006SkpGQ6InOpUqW47777mDdvHrfffjvz5s2jX79+jufj4+Np164dEyZMuOLY0qVLZ69wERFxap98Atu2QVAQjB5tdTUKN9dls0GhQlZXcXO2bNmC3W5n0qRJuLmZF+u++OKL6x7XtWtXhg4dSufOndm/fz+dOnVyPHfbbbfxv//9jwoVKuDhob9GIiIFVUwMvPKKuTxqFBQvbm09oAbFBULlypVJTU1l6tSp7N+/n88++4wZM2Zc97hHHnmEuLg4+vXrx7333kuZMmUcz/Xv35+zZ8/SuXNnfv/9d/79919WrlxJz549NXeUiEgBMnYsnDoF1apB//5WV2NSuCkA6tWrx+TJk5kwYQK1a9dm7ty5REREXPe4woUL065dO7Zt20bXrl0zPFemTBl+/fVX0tPTadmyJXXq1GHQoEEEBQU5rg6JiIhr27cPpkwxlydNgstaR1jGZhjZ7XDs3GJjYwkMDCQmJoaAgIAMz50/f54DBw5QsWJFfHx8LKrQdennKyLiWh55BBYvhpYtYcUKsylHbrnW9/fl9E9sERERybY1a8xg4+4OkyfnbrDJLoUbERERyZb0dBg0yFzOD12/L6dwIyIiItny8cewfXv+6fp9OYUbERERybKYGHj1VXP5tdegWDFLy8mUwo2IiIhk2aVdv5991upqMqdwIyIiIllyadfvyZPzT9fvyynciIiISJa89BKkpkKrVtC6tdXVXJ3CjYiIiFzXjz/CkiX5s+v35RRuRERE5Jou7frdrx/UrGlpOdelcFPAvPbaa9SvX9/qMkRExIl8/DHs2AFFipg9pPI7hRsXcc899zDoQqy+hhdffJHVq1c71nv06EGHDh1yrzAREXFqztD1+3IeVhcgecMwDNLT0/H398ff39/qckRExEm88YbZ9bt6dfOWlDPQlRsX0KNHD3766SfeeecdbDYbNpuN2bNnY7PZWL58OQ0aNMDb25t169ZluC312muvMWfOHL7++mvHcWvXrgVgx44d3Hffffj6+lKsWDGeeeYZ4uPjM7xnhw4deOuttyhdujTFihWjf//+pKamWvATEBGR3PDPP/DOO+Zyfu76fTldubkOwzBITE205L39PP2wZaE5+jvvvMPevXupXbs2Y8aMAeCvv/4CYPjw4bz11lvceuutFClSxBFewLxFtWvXLmJjY5k1axYARYsWJSEhgVatWtG0aVN+//13Tp48Se/evXnuueeYPXu24/g1a9ZQunRp1qxZw759++jYsSP169enT58+OfdDEBERy1zo+v3AA/m76/flFG6uIzE1Ef8Ia27jxIfHU8ir0HX3CwwMxMvLCz8/P0qVKgXA7t27ARgzZgz3339/psf5+/vj6+tLcnKy4ziAOXPmcP78eT799FMKFTLff9q0abRr144JEyYQHBwMQJEiRZg2bRru7u5Ur16dNm3asHr1aoUbEREXsHo1fP31xa7fzkS3pVxcw4YNs33Mrl27qFevniPYADRr1gy73c6ePXsc22rVqoW7u7tjvXTp0pw8efLmChYREculpcHgwebys89CjRrW1pNdunJzHX6efsSHx19/x1x675t1aUDJaZ6X3Xy12WzY7fZcez8REckbl3b9HjXK6mqyT+HmOmw2W5ZuDVnNy8uL9PT0HDmuRo0azJ49m4SEBEc4+vXXX3Fzc6NatWo5Uq+IiORP0dEXu36PHu0cXb8vp9tSLqJChQps3LiRgwcPcvr06SxfQalQoQLbt29nz549nD59mtTUVLp27YqPjw/du3dn586drFmzhgEDBvDUU0852tuIiIhreuMNOH3avBXVt6/V1dwYhRsX8eKLL+Lu7k7NmjUpUaIEhw8fztJxffr0oVq1ajRs2JASJUrw66+/4ufnx8qVKzl79iyNGjXiscceo0WLFkybNi2Xz0JERKz0zz/w7rvmsjN1/b6czTAMw6o3//nnn3nzzTfZsmULJ06cYPHixVkeLffXX3/l7rvvpnbt2mzdujXL7xkbG0tgYCAxMTEEBARkeO78+fMcOHCAihUr4uPjk40zkazQz1dEJH9r3x6WLjW7fS9bZnU1GV3r+/tyll65SUhIoF69ekyfPj1bx0VHR9OtWzdatGiRS5WJiIgULD/8YAYbd3eYNMnqam6OpQ2KW7duTesbGBWob9++dOnSBXd3d5YsWXLNfZOTk0lOTnasx8bGZvv9REREXNmlXb/793e+rt+Xc7o2N7NmzWL//v2MymLftIiICAIDAx2PkJCQXK5QRETEuXz0EezcCUWLOmfX78s5Vbj5559/GD58OJ9//jkeHlm76BQeHk5MTIzjceTIkVyuUkRExHlER8OIEeby6NFmwHF2TjPOTXp6Ol26dGH06NFUrVo1y8d5e3vj7e2di5WJiIg4r9dfv9j1+//+z+pqcobThJu4uDg2b97Mn3/+yXPPPQeA3W7HMAw8PDz4/vvvue+++yyuUkRExHns3esaXb8v5zThJiAggB07dmTY9t577/Hjjz/y1VdfUbFiRYsqExER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W2+9RXx8PE888cRV94mIiCAwMNDxCAkJycMKRUREbs7o0XDuHNSpA08/bXU1zsFpw828efMYPXo0X3zxBSVLlrzqfuHh4cTExDgeR44cycMqRUREbtzff8N775nL6vqddU75Y1qwYAG9e/fmyy+/JCws7Jr7ent74+3tnUeViYiI5JwLXb/bt4f77rO6GufhdFdu5s+fT8+ePZk/fz5t2rSxuhwREZFcsXy5OfO3pye8+abV1TgXS6/cxMfHs2/fPsf6gQMH2Lp1K0WLFqVcuXKEh4dz7NgxPv30U8C8FdW9e3feeecdmjRpQmRkJAC+vr4EBgZacg4iIiI57dKu3wMHqut3dll65Wbz5s2EhoYSGhoKwJAhQwgNDXV06z5x4gSHDx927P/hhx+SlpZG//79KV26tOMxcOBAS+oXERHJDe+/D7t3Q4kS8OqrVlfjfPLNODd5RePciIhIfnbmjHml5tw5+OADeOYZqyvKH7Lz/e10bW5ERERc2WuvmcGmbl11/b5RCjciIiL5xN9/m7ekwOz67e5uaTlOS+FGREQkHzAMsxFxejp06AD33mt1Rc5L4UZERCQfWL4cVq5U1++coHAjIiJisUu7fg8aBJUrW1qO01O4ERERsdh778GePWbX71desboa56dwIyIiYqEzZ8weUgBjx4LGpL15CjciIiIWGjUKoqPNrt+9elldjWtQuBEREbHIX3/BjBnmsrp+5xyFGxEREQtc2vX74YfV9TsnKdyIiIhYYNky+P578PJS1++cpnAjIiKSxy7v+l2pkqXluByFGxERkTw2fTrs3QslS6rrd25QuBEREclDp0/D6NHm8htvwHUmuJYboHAjIiKShy50/a5XT12/c4vCjYiISB7ZuVNdv/OCwo2IiEgeuND1226HRx6Be+6xuiLXpXAjIiKSB777DlatMrt+T5xodTWuTeFGREQkl6WkwAsvmMvq+p37FG5ERERymbp+5y2FGxERkVx0adfvsWPV9TsvKNyIiIjkopEjISYG6teHnj2trqZgULgRERHJJTt3wgcfmMvq+p13FG5ERERygWHA4MFm1+9HH4W777a6ooJD4UZERCQXfPst/PCDun5bQeFGREQkh13a9XvwYLj1VmvrKWgUbkRERHLYtGnwzz8QHAwvv2x1NQWPwo2IiEgOOnUKxowxl9X12xoKNyIiIjlo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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "muon:\n",
      "        N     naive    triton      cuda\n",
      "0  1024.0  0.010073  0.012151  0.003139\n",
      "1  2048.0  0.028228  0.030456  0.002796\n",
      "2  3072.0  0.078057  0.084861  0.003088\n",
      "3  4096.0  0.181412  0.120780  0.002789\n",
      "4  5120.0  0.370362  0.246647  0.003091\n",
      "5  6144.0  0.620314  0.388048  0.003050\n",
      "6  7168.0  1.027804  0.634725  0.003075\n",
      "7  8192.0  1.544641  0.903523  0.003065\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['N'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[1024 * i for i in range(1, 8+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['naive', 'triton',\"cuda\"],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"naive\",\n",
    "            \"triton\",\n",
    "            \"cuda\"\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-'), ('red', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"muon\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(N, provider):\n",
    "    device = \"cuda\"\n",
    "    dtype = torch.bfloat16\n",
    "    x = torch.randn(N, N, device=device, dtype=dtype)\n",
    "    out = torch.empty_like(x)\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'naive':\n",
    "        ms = triton.testing.do_bench(lambda: torch.matmul(x, x.t()))\n",
    "    if provider == 'triton':\n",
    "        ms = triton.testing.do_bench(lambda: matmul(x, out))\n",
    "    if provider == 'cuda':\n",
    "        ms = triton.testing.do_bench(lambda: matmul_transpose(x))\n",
    "    return ms\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjIAAAGwCAYAAACzXI8XAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAVvxJREFUeJzt3Xd4FPXaxvHvpidAEkJJiALSUXpTEZUWqYI0ReQgKFgRQUWKdJBiwYIiFjwQFBAQKSKCdMUXUFAgoHQQBJLQ0kndef+YQyAKSsnu7G7uz3Xt5ezO7MwzWU5yn9nfbx6bYRgGIiIiIm7Iy+oCRERERK6XgoyIiIi4LQUZERERcVsKMiIiIuK2FGRERETEbSnIiIiIiNtSkBERERG35WN1AY5mt9s5ceIERYoUwWazWV2OiIiIXAXDMEhOTiYyMhIvrytfd/H4IHPixAlKly5tdRkiIiJyHY4dO8bNN998xfUeH2SKFCkCmD+I4OBgi6sRERGRq5GUlETp0qVz/45ficcHmQtfJwUHByvIiIiIuJl/Gxaiwb4iIiLithRkRERExG0pyIiIiIjb8vgxMlcrJyeHrKwsq8vwGL6+vnh7e1tdhoiIeLgCH2QMwyA2NpaEhASrS/E4oaGhRERE6P49IiLiMAU+yFwIMSVLliQoKEh/dPOBYRikpaURHx8PQKlSpSyuSEREPFWBDjI5OTm5IaZYsWJWl+NRAgMDAYiPj6dkyZL6mklERByiQA/2vTAmJigoyOJKPNOFn6vGHomIiKMU6CBzgb5Ocgz9XEVExNEUZERERMRtKciIiIiI21KQKaBmzpxJaGio1WWIiIjcEAWZAqpr167s27fP6jJERMSNnTwJv/5qbQ0KMgVUYGAgJUuWtLoMERFxY6NGQd26MGGCdTUoyFzCMCA11ZqHYVxbrU2aNOH5559n0KBBhIWFERERwejRo3PXv/XWW9SoUYNChQpRunRpnn32WVJSUnLXX/rV0r59+7DZbOzZsyfPMd5++20qVKiQ+3zXrl20bt2awoULEx4eTo8ePTh9+vQ1/5xFRMT97d0L//2vudy4sXV1KMhcIi0NChe25pGWdu31RkdHU6hQIbZs2cLrr7/O2LFjWbVqFQBeXl5MmTKF3bt3Ex0dzdq1axk0aNBl91O5cmXq16/P7Nmz87w+e/ZsHnnkEQASEhJo1qwZderUYevWraxYsYK4uDgeeuihay9cRETc3vDhkJMD7dpBo0YWFmJ4uMTERAMwEhMT/7bu/Pnzxm+//WacP3/eMAzDSEkxDPPaiPMfKSnXdl6NGzc27r777jyvNWjQwBg8ePBlt1+wYIFRrFix3OczZswwQkJCcp+//fbbRoUKFXKf79271wCM33//3TAMwxg3bpzRokWLPPs8duyYARh79+697DH/+vMVERHP8NNP5t8um80wYmIcc4x/+vt9qQLdouCvgoLgkm9fnH7sa1WzZs08z0uVKpXb32j16tVMnDiRPXv2kJSURHZ2Nunp6aSlpV32TsYPP/wwAwcOZPPmzdx5553Mnj2bunXrUrVqVQB27NjBunXrKFy48N/ee/DgQSpXrnztJyAiIm5p6FDzv48+CtWrW1uLgswlbDYoVMjqKq6er69vnuc2mw273c6RI0e4//77eeaZZxg/fjxhYWFs3LiR3r17k5mZedkgExERQbNmzZgzZw533nknc+bM4Zlnnsldn5KSQrt27Xjttdf+9l41hRQRKThWrYI1a8DPD8aMsboaBRmPtG3bNux2O5MnT8bLyxwGNX/+/H99X/fu3Rk0aBDdunXj0KFDPPzww7nr6taty8KFC7nlllvw8dE/GxGRgshuv3g15tlnoWxZa+sBDfb1SBUrViQrK4v33nuPQ4cO8dlnn/Hhhx/+6/s6depEcnIyzzzzDE2bNiUyMjJ3Xd++fTl79izdunXj559/5uDBg6xcuZLHHnuMnJwcR56OiIi4iC+/hG3boEgReOUVq6sxKch4oFq1avHWW2/x2muvUb16dWbPns3EiRP/9X1FihShXbt27Nixg+7du+dZFxkZyY8//khOTg4tWrSgRo0aDBgwgNDQ0NyrPiIi4rmysmDYMHN54EAoUcLaei6wGca13sHEvSQlJRESEkJiYiLBwcF51qWnp3P48GHKlStHQECARRV6Lv18RUQ8x0cfwdNPmwHm0CHz1iGO9E9/vy+l/ystIiIi/ygt7eLA3hEjHB9iroWCjIiIiPyjKVPMvkq33AJPPWV1NXkpyIiIiMgVnT0LkyaZy+PGmdOuXYmCjIiIiFzRpEmQmAg1a8L/uta4FAUZERERuaw//4T33jOXJ04EV5yk6oIliYiIiCsYMwbS0+Gee6B1a6uruTwFGREREfmbPXvgv/81lydN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WOHU873U0qVLjW+++cbYt2+fsXfvXuOVV14xfH19jV27dhmG4bnn/Vc//fSTccsttxg1a9Y0+vfvn/u6p57/qFGjjGrVqhknT57MfZw6dSp3vaeet2EYxtmzZ42yZcsavXr1MrZs2WIcOnTIWLlypXHgwIHcbTz1d118fHyez3zVqlUGYKxbt84wDM/+3MePH28UK1bMWLZsmXH48GFjwYIFRuHChY133303dxtP+twLbJC51F+DjN1uNyIiIow33ngj97WEhATD39/fmDt3rmEYhvHbb78ZgPHzzz/nbvPtt98aNpvNOH78uGEYhvHBBx8YRYsWNTIyMnK3GTx4sFGlShUHn9HVi4+PNwBjw4YNhmGY5+nr62ssWLAgd5vff//dAIxNmzYZhmGGQC8vLyM2NjZ3m2nTphnBwcG55zpo0CCjWrVqeY7VtWtXo2XLlo4+pWtStGhRY/r06QXmvJOTk41KlSoZq1atMho3bpwbZDz5/EeNGmXUqlXrsus8+bwNw/x9c/fdd19xfUH6Xde/f3+jQoUKht1u9/jPvW3btsbjjz+e57VOnToZ3bt3NwzD8z73AvvV0j85fPgwsbGxREVF5b4WEhLCHXfcwaZNmwDYtGkToaGh1K9fP3ebqKgovLy82LJlS+429957L35+frnbtGzZkr1793Lu3Dknnc0/S0xMBCAsLAyAbdu2kZWVlefcq1atSpkyZfKce40aNQgPD8/dpmXLliQlJbF79+7cbS7dx4VtLuzDajk5OXzxxRekpqbSsGHDAnPeffv2pW3btn+r0dPPf//+/URGRlK+fHm6d+/O0aNHAc8/76VLl1K/fn0efPBBSpYsSZ06dfjkk09y1xeU33WZmZl8/vnnPP7449hsNo//3O+66y7WrFnDvn37ANixYwcbN26kdevWgOd97goylxEbGwuQ5x/whecX1sXGxlKyZMk86318fAgLC8uzzeX2cekxrGS32xkwYACNGjWievXqgFmXn58foaGhebb967n/23ldaZukpCTOnz/viNO5KjExMRQuXBh/f3+efvppFi1axG233ebx5w3wxRdf8MsvvzBx4sS/rfPk87/jjjuYOXMmK1asYNq0aRw+fJh77rmH5ORkjz5vgEOHDjFt2jQqVarEypUreeaZZ3j++eeJjo4GCs7vusWLF5OQkECvXr0Az/73DjBkyBAefvhhqlatiq+vL3Xq1GHAgAF0794d8LzP3eO7X8uV9e3bl127drFx40arS3GaKlWqsH37dhITE/nyyy/p2bMnGzZssLoshzt27Bj9+/dn1apVBAQEWF2OU134f6EANWvW5I477qBs2bLMnz+fwMBACytzPLvdTv369ZkwYQIAderUYdeuXXz44Yf07NnT4uqc59NPP6V169ZERkZaXYpTzJ8/n9mzZzNnzhyqVavG9u3bGTBgAJGRkR75ueuKzGVEREQA/G0Ee1xcXO66iIgI4uPj86zPzs7m7Nmzeba53D4uPYZVnnvuOZYtW8a6deu4+eabc1+PiIggMzOThISEPNv/9dz/7byutE1wcLClfzz8/PyoWLEi9erVY+LEidSqVYt3333X489727ZtxMfHU7duXXx8fPDx8WHDhg1MmTIFHx8fwsPDPfr8LxUaGkrlypU5cOCAx3/upUqV4rbbbsvz2q233pr71VpB+F33xx9/sHr1avr06ZP7mqd/7i+//HLuVZkaNWrQo0cPXnjhhdyrsZ72uSvIXEa5cuWIiIhgzZo1ua8lJSWxZcsWGjZsCEDDhg1JSEhg27ZtudusXbsWu93OHXfckbvN999/T1ZWVu42q1atokqVKhQtWtRJZ5OXYRg899xzLFq0iLVr11KuXLk86+vVq4evr2+ec9+7dy9Hjx7Nc+4xMTF5/pGvWrWK4ODg3F+aDRs2zLOPC9tc2IersNvtZGRkePx5N2/enJiYGLZv3577qF+/Pt27d89d9uTzv1RKSgoHDx6kVKlSHv+5N2rU6G+3V9i3bx9ly5YFPPt33QUzZsygZMmStG3bNvc1T//c09LS8PLK++fd29sbu90OeODn7tShxS4kOTnZ+PXXX41ff/3VAIy33nrL+PXXX40//vjDMAxzalpoaKixZMkSY+fOncYDDzxw2alpderUMbZs2WJs3LjRqFSpUp6paQkJCUZ4eLjRo0cPY9euXcYXX3xhBAUFWTol8ZlnnjFCQkKM9evX55mamJaWlrvN008/bZQpU8ZYu3atsXXrVqNhw4ZGw4YNc9dfmJbYokULY/v27caKFSuMEiVKXHZa4ssvv2z8/vvvxtSpUy2fljhkyBBjw4YNxuHDh42dO3caQ4YMMWw2m/Hdd98ZhuG5530ll85aMgzPPf+XXnrJWL9+vXH48GHjxx9/NKKioozixYsb8fHxhmF47nkbhjnV3sfHxxg/fryxf/9+Y/bs2UZQUJDx+eef527jqb/rDMMwcnJyjDJlyhiDBw/+2zpP/tx79uxp3HTTTbnTr7/66iujePHixqBBg3K38aTPvcAGmXXr1hnA3x49e/Y0DMOcnjZixAgjPDzc8Pf3N5o3b27s3bs3zz7OnDljdOvWzShcuLARHBxsPPbYY0ZycnKebXbs2GHcfffdhr+/v3HTTTcZkyZNctYpXtblzhkwZsyYkbvN+fPnjWeffdYoWrSoERQUZHTs2NE4efJknv0cOXLEaN26tREYGGgUL17ceOmll4ysrKw826xbt86oXbu24efnZ5QvXz7PMazw+OOPG2XLljX8/PyMEiVKGM2bN88NMYbhued9JX8NMp56/l27djVKlSpl+Pn5GTfddJPRtWvXPPdR8dTzvuDrr782qlevbvj7+xtVq1Y1Pv744zzrPfV3nWEYxsqVKw3gb+djGJ79uSclJRn9+/c3ypQpYwQEBBjly5c3hg0blmeatCd97jbDuORWfyIiIiJuRGNkRERExG0pyIiIiIjbUpARERERt6UgIyIiIm5LQUZERETcloKMiIiIuC0FGREREXFbCjIiIiLithRkRERExG0pyIiIW+nVqxc2m41JkybleX3x4sXYbDaLqhIRqyjIiIjbCQgI4LXXXuPcuXNWlyIiFlOQERG3ExUVRUREBBMnTrS6FBGxmIKMiLgdb29vJkyYwHvvvceff/5pdTkiYiEFGRFxSx07dqR27dqMGjXK6lJExEIKMiLitl577TWio6P5/fffrS5FRCyiICMibuvee++lZcuWDB061OpSRMQiPlYXICJyIyZNmkTt2rWpUqWK1aWIiAV0RUZE3FqNGjXo3r07U6ZMsboUEbGAgoyIuL2xY8dit9utLkNELGAzDMOwuggRERGR66ErMiIiIuK2FGRERETEbSnIiIiIiNtSkBERERG3pSAjIiIibktBRkRERNyWgoyIiIi4LQUZERERcVsKMiIiIuK2FGRERETEbSnIiIiIiNv6f9+eAN5dWRgcAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "muon:\n",
      "        N      naive     triton\n",
      "0  1024.0   0.282652   0.609693\n",
      "1  2048.0   0.572634   0.671413\n",
      "2  3072.0   1.455426   1.437495\n",
      "3  4096.0   3.053827   2.699235\n",
      "4  5120.0   5.814272   4.953599\n",
      "5  6144.0   9.623940   7.736847\n",
      "6  7168.0  14.736821  11.981832\n",
      "7  8192.0  22.842152  17.418835\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['N'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[1024 * i for i in range(1, 8+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['naive', 'triton'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"naive\",\n",
    "            \"triton\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"muon\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(N, provider):\n",
    "    device = \"cuda\"\n",
    "    dtype = torch.bfloat16\n",
    "    x = torch.randn(N, N, device=device, dtype=dtype)\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'naive':\n",
    "        ms = triton.testing.do_bench(lambda: run_torch(x))\n",
    "    if provider == 'triton':\n",
    "        ms = triton.testing.do_bench(lambda: run_triton(x))\n",
    "    return ms\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
